

var Watcher = function Watcher (
	vm,
	expOrFn,
	cb,
	options,
	isRenderWatcher
) {

	this.vm = vm;

	if (isRenderWatcher) {
		vm._watcher = this;
	}

	vm._watchers.push(this);

	console.log('Watcher---constructor---options----->',options,);


	// options
	if (options) {
		this.deep = !!options.deep;
		this.user = !!options.user;
		this.lazy = !!options.lazy;
		this.sync = !!options.sync;
	} else {
		this.deep = this.user = this.lazy = this.sync = false;
	}

	this.cb = cb;
	this.id = ++uid$1; // uid for batching
	this.active = true;
	this.dirty = this.lazy; // for lazy watchers
	this.deps = [];
	this.newDeps = [];
	this.depIds = new _Set();
	this.newDepIds = new _Set();
	this.expression = expOrFn.toString();

	// parse expression for getter
	if (typeof expOrFn === 'function') {
		this.getter = expOrFn;
	} else {

		this.getter = parsePath(expOrFn);

		if (!this.getter) {

			this.getter = function () {};

			"development" !== 'production' && warn(
				"Failed watching path: \"" + expOrFn + "\" " +
				'Watcher only accepts simple dot-delimited paths. ' +
				'For full control, use a function instead.',
				vm
			);

		}

	}

	// learn:默认实例化的时候，先执行一次更新
	this.value = this.lazy? undefined: this.get();
};

/**
 * Parse simple path.
 */
var bailRE = /[^\w.$]/;

function parsePath (path) {

	if (bailRE.test(path)) {
		return
	}

	var segments = path.split('.');
	
	return function (obj) {
		for (var i = 0; i < segments.length; i++) {
			if (!obj) { return }
			obj = obj[segments[i]];
		}
		return obj
	}
}

/**
 * Evaluate the getter, and re-collect dependencies.
 * 
 * learn：执行更新函数，并且返回值？
 */
Watcher.prototype.get = function get () {

	pushTarget(this);

	var value;
	var vm = this.vm;

	try {

		// learn：this.getter 等于 传入 watcher的 函数，比如 updateComponent
		// learn：this.getter = updateComponent=> vm._update(vm._render(), hydrating);
		value = this.getter.call(vm, vm);

	} catch (e) {
		if (this.user) {
			handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
		} else {
			throw e
		}
	} finally {
		
		// "touch" every property so they are all tracked as
		// dependencies for deep watching
		if (this.deep) {
			traverse(value);
		}
		popTarget();
		this.cleanupDeps();
	}

	return value

};

/**
 * Add a dependency to this directive.
 * learn：给 watcher 添加 dep实例
 * learn：因为每一个属性有一个 dep实例，所以主要是为了保存 看有多少属性被监听
 */
Watcher.prototype.addDep = function addDep (dep) {

	var id = dep.id;

	// learn：如果同一个属性，变化两次，会给 dep 添加两次 this
	// learn：所以需要判断是否添加过
	if (!this.newDepIds.has(id)) {

		this.newDepIds.add(id);
		this.newDeps.push(dep);

		if (!this.depIds.has(id)) {
			dep.addSub(this);
		}

	}

};

/**
 * Clean up for dependency collection.
 */
Watcher.prototype.cleanupDeps = function cleanupDeps () {

	var this$1 = this;

	var i = this.deps.length;

	while (i--) {
		var dep = this$1.deps[i];
		if (!this$1.newDepIds.has(dep.id)) {
			dep.removeSub(this$1);
		}
	}

	var tmp = this.depIds;

	this.depIds = this.newDepIds;
	this.newDepIds = tmp;
	this.newDepIds.clear();
	tmp = this.deps;
	this.deps = this.newDeps;
	this.newDeps = tmp;
	this.newDeps.length = 0;

};

/**
 * Subscriber interface.
 * Will be called when a dependency changes.
 */
Watcher.prototype.update = function update () {

	console.log('this.lazy--->',this.lazy,);
	console.log('this.sync--->',this.sync,);
	// console.log('this.lazy--->',this.lazy,);


	/* istanbul ignore else */
	if (this.lazy) {
		this.dirty = true;
	} else if (this.sync) {
		this.run();
	} else {
		queueWatcher(this);
	}
};

/**
 * Scheduler job interface.
 * Will be called by the scheduler.
 */
Watcher.prototype.run = function run () {

	// 
	if (this.active) {

		// learn：会执行 传入的 updateComponent
		// learn：updateComponent => vm._update(vm._render(), hydrating)
        var value = this.get();
        
		if (
			value !== this.value ||
			// Deep watchers and watchers on Object/Arrays should fire even
			// when the value is the same, because the value may
			// have mutated.
			isObject(value) ||
			this.deep
		) {

			// set new value
			var oldValue = this.value;
			this.value = value;
			
			if (this.user) {
				try {
					this.cb.call(this.vm, value, oldValue);
				} catch (e) {
					handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\""));
				}
			} else {
				this.cb.call(this.vm, value, oldValue);
			}

		}
	}
};

/**
 * Evaluate the value of the watcher.
 * This only gets called for lazy watchers.
 */
Watcher.prototype.evaluate = function evaluate () {
	this.value = this.get();
	this.dirty = false;
};

/**
 * Depend on all deps collected by this watcher.
 */
Watcher.prototype.depend = function depend () {
	var this$1 = this;

	var i = this.deps.length;
	while (i--) {
		this$1.deps[i].depend();
	}
};

/**
 * Remove self from all dependencies' subscriber list.
 */
Watcher.prototype.teardown = function teardown () {
		var this$1 = this;

	if (this.active) {
		// remove self from vm's watcher list
		// this is a somewhat expensive operation so we skip it
		// if the vm is being destroyed.
		if (!this.vm._isBeingDestroyed) {
			remove(this.vm._watchers, this);
		}
		var i = this.deps.length;
		while (i--) {
			this$1.deps[i].removeSub(this$1);
		}
		this.active = false;
	}
};


// learn：保存 监听者的 this
function pushTarget (_target) {

	// set var targetStack = []; 711
	if (Dep.target) { targetStack.push(Dep.target); }
	Dep.target = _target;
}

function popTarget () {
	Dep.target = targetStack.pop();
}


/**
 * Push a watcher into the watcher queue.
 * Jobs with duplicate IDs will be skipped unless it's
 * pushed when the queue is being flushed.
 * 
 * Learn：把监听者 放入队列中，相同 ID 会被跳过，除非他是被保存过
 */
function queueWatcher (watcher) {

	var id = watcher.id;
	
	console.log('watcher.id--->',watcher.id,has[id],);
    
    // set: var has = {}; 29
	if (has[id] == null) {

        has[id] = true;
        
		console.log('!flushing)--->',!flushing),);
    
        // set: var flushing = false; 2947
		if (!flushing) {

            // set: var queue = [];  2942
            queue.push(watcher);
            
		} else {

			// if already flushing, splice the watcher based on its id
			// if already past its id, it will be run next immediately.
			var i = queue.length - 1;

			while (i > index && queue[i].id > watcher.id) {
				i--;
            }
            
            queue.splice(i + 1, 0, watcher);
            
        }

        // queue the flush 
        // set: var waiting = false; 2946
		if (!waiting) {
			waiting = true;
			nextTick(flushSchedulerQueue);
        }
        
	}
}


function nextTick (cb, ctx) {

	var _resolve;

	callbacks.push(function () {
		if (cb) {
			try {
				cb.call(ctx);
			} catch (e) {
				handleError(e, ctx, 'nextTick');
			}
		} else if (_resolve) {
			_resolve(ctx);
		}
	});

	// set：var pending = false; 1754
	if (!pending) {
		pending = true;

		// set: var useMacroTask = false; 1775
		if (useMacroTask) {
			macroTimerFunc();
		} else {
			microTimerFunc();
		}
	}

	// $flow-disable-line
	if (!cb && typeof Promise !== 'undefined') {
		return new Promise(function (resolve) {
			_resolve = resolve;
		})
	}

}

/**
 * Flush both queues and run the watchers.
 * 
 *  LEARN:  遍历队列，并且运行 监听者 watcher.run() , 并且触发 updated 和 activated 钩子
 * 
 */
function flushSchedulerQueue () {

    flushing = true;
    
	var watcher, id;

	// Sort queue before flush.
	// This ensures that:
	// 1. Components are updated from parent to child. (because parent is always
	//    created before the child)
	// 2. A component's user watchers are run before its render watcher (because
	//    user watchers are created before the render watcher)
	// 3. If a component is destroyed during a parent component's watcher run,
    //    its watchers can be skipped.
    
	// LEARN: 
	// LEARN: 队列在遍历前，先进行排序
	// LEARN: 1、为了确保父组件 比子组件 先更新，因为父组件总是先 创建
	// LEARN: 2、组件的 user watcher 总是比 render watcher 要 先运行
	// LEARN: 3、如果一个组件被销毁的时候，他的父组件仍然获取，那么相应的组件坚挺着要被跳过

	queue.sort(function (a, b) { return a.id - b.id; });

	// do not cache length because more watchers might be pushed
	// as we run existing watchers

	//  LEARN:  不保存长度，是因为数组的长度可能会发生变化
	for (index = 0; index < queue.length; index++) {

		watcher = queue[index];
		id = watcher.id;

		has[id] = null;

        watcher.run();
        
		// in dev build, check and stop circular updates.
		if ("development" !== 'production' && has[id] != null) {

			// set var circular = {}; 2950
            circular[id] = (circular[id] || 0) + 1;
            
			if (circular[id] > MAX_UPDATE_COUNT) {
				warn(
					'You may have an infinite update loop ' + (
						watcher.user
							? ("in watcher with expression \"" + (watcher.expression) + "\"")
							: "in a component render function."
					),
					watcher.vm
				);
				break
            }
            
		}
	}

	// keep copies of post queues before resetting state

	// LEARN: 在重置之前，复制一份出来，为了不跟作为保存的有冲突，先复制出来，再清空储物仓 
	var activatedQueue = activatedChildren.slice(); // var activatedChildren = [];2944
	var updatedQueue = queue.slice();

	// LEARN：重置变量
	resetSchedulerState();

	// call component updated and activated hooks
	// LEARN: 触发 updated 和 activated 钩子
	callActivatedHooks(activatedQueue);
	callUpdatedHooks(updatedQueue);

	// devtool hook
	/* istanbul ignore if */
	if (devtools && config.devtools) {
		devtools.emit('flush');
    }
    
}

// LEARN：activated 钩子
function callActivatedHooks (queue) {
	for (var i = 0; i < queue.length; i++) {
		queue[i]._inactive = true;
		activateChildComponent(queue[i], true /* true */);
	}
}

// LEARN： updated 钩子
function callUpdatedHooks (queue) {
	var i = queue.length;
	while (i--) {
		var watcher = queue[i];
		var vm = watcher.vm;
		if (vm._watcher === watcher && vm._isMounted) {
			callHook(vm, 'updated');
		}
	}
}



/**
 * Reset the scheduler's state.
 * Learn：初始化变量
 */
function resetSchedulerState () {

	index = queue.length = activatedChildren.length = 0;

	has = {};

	{
		circular = {};
	}

	waiting = flushing = false;
}